package com.music.glide.load.engine.bitmap_recycle

import android.graphics.Bitmap
import android.os.Build
import com.music.glide.util.Util
import java.util.NavigableMap
import java.util.TreeMap

class KSizeConfigStrategy : KLruPoolStrategy {

    private val keyPool by lazy { KeyPool() }

    private val groupMap by lazy { KGroupLinkedMap<Key, Bitmap>() }

    private val sortedSizes by lazy { mutableMapOf<Bitmap.Config?, NavigableMap<Int, Int>>() }


    override fun put(bitmap: Bitmap) {
        val size = Util.getBitmapByteSize(bitmap)
        val key = keyPool.get(size, bitmap.config)
        groupMap.put(key, bitmap)
        val sizes = getSizesForConfig(bitmap.config)
        val current = sizes[size]
        sizes[size] = if (current == null) 1 else current + 1
    }

    override fun get(width: Int, height: Int, config: Bitmap.Config): Bitmap? {
        val size = Util.getBitmapByteSize(width, height, config)
        val key = findBestKey(size, config)
        val result = groupMap.get(key)
        if (result != null) {
            decrementBitmapOfSize(key.size, result)
            result.reconfigure(width, height, config)
        }
        return result
    }

    override fun removeLast(): Bitmap? {
        val removed = groupMap.removeLast()
        if (removed != null) {
            val size = Util.getBitmapByteSize(removed)
            decrementBitmapOfSize(size, removed)
        }
        return removed
    }

    override fun logBitmap(bitmap: Bitmap): String {
        return getBitmapString(bitmap)
    }

    override fun logBitmap(width: Int, height: Int, config: Bitmap.Config?): String {
        return getBitmapString(Util.getBitmapByteSize(width, height, config), config)
    }

    override fun getSize(bitmap: Bitmap): Int {
        return Util.getBitmapByteSize(bitmap)
    }

    /**
     * 疑问config不同,更新key???
     */
    private fun findBestKey(size: Int, config: Bitmap.Config?): Key {
        var key = keyPool.get(size, config)
        run {
            getInConfigs(config).forEach {
                val navigableMap = getSizesForConfig(config)
                val possibleSize = navigableMap.ceilingKey(size)
                if (possibleSize != null && possibleSize < size * MAX_SIZE_MULTIPLE) {
                    if (possibleSize != size || (if (it == null) config != null else it != config)) {
                        keyPool.offer(key)
                        key = keyPool.get(possibleSize, it)
                        return@run
                    }
                }
            }
        }
        return key
    }

    private fun getSizesForConfig(config: Bitmap.Config?): NavigableMap<Int, Int> {
        var sizes = sortedSizes[config]
        if (sizes == null) {
            sizes = TreeMap()
            sortedSizes[config] = sizes
        }
        return sizes
    }

    private fun decrementBitmapOfSize(size: Int, bitmap: Bitmap) {
        val sizes = getSizesForConfig(bitmap.config)
        val current = sizes[size] ?: throw IllegalStateException("Tried to decrement empty size")

        if (current == 1) {
            sizes.remove(size)
        } else {
            sizes[size] = current - 1
        }
    }

    private class KeyPool : KBaseKeyPool<Key>() {

        fun get(size: Int, config: Bitmap.Config?): Key {
            return get().apply { init(size, config) }
        }

        override fun create(): Key {
            return Key(this)
        }
    }

    private class Key(private val pool: KeyPool) : KPoolAble {

        private var config: Bitmap.Config? = null

        var size = 0

        fun init(size: Int, config: Bitmap.Config?) {
            this.size = size;
            this.config = config
        }


        override fun offer() {
            pool.offer(this)
        }

        override fun toString(): String {
            return getBitmapString(size, config)
        }
    }

    companion object {

        const val MAX_SIZE_MULTIPLE = 8

        private val ARGB_8888_CONFIGS = mutableListOf(Bitmap.Config.ARGB_8888, null).apply {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                add(Bitmap.Config.RGBA_F16)
            }
        }.toTypedArray()
        private val RGBA_F16_IN_CONFIGS = ARGB_8888_CONFIGS

        private val RAG_565_IN_CONFIGS = arrayOf<Bitmap.Config?>(Bitmap.Config.RGB_565)

        private val ARGB_4444_IN_CONFIGS = arrayOf<Bitmap.Config?>(Bitmap.Config.ARGB_4444)

        private val ALPHA_8_IN_CONFIGS = arrayOf<Bitmap.Config?>(Bitmap.Config.ALPHA_8)

        fun getBitmapString(size: Int, config: Bitmap.Config?): String {
            return "[${size}]($config)"
        }

        fun getBitmapString(bitmap: Bitmap): String {
            return "[${Util.getBitmapByteSize(bitmap)}]"
        }

        fun getInConfigs(requested: Bitmap.Config?): Array<Bitmap.Config?> {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                if (Bitmap.Config.RGBA_F16 == requested) {
                    return RGBA_F16_IN_CONFIGS
                }
            }
            return when (requested) {
                Bitmap.Config.ARGB_8888 -> ARGB_8888_CONFIGS
                Bitmap.Config.ARGB_4444 -> ARGB_4444_IN_CONFIGS
                Bitmap.Config.RGB_565 -> RAG_565_IN_CONFIGS
                Bitmap.Config.ALPHA_8 -> ALPHA_8_IN_CONFIGS
                else -> arrayOf(requested)
            }
        }
    }
}